home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Programming / SWI / source / html / tex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-20  |  58.0 KB  |  2,681 lines

  1. /*  $Id: tex.c,v 1.1 1997/01/20 16:36:27 jan Exp $
  2.  
  3.     Designed and implemented by Jan Wielemaker
  4.     E-mail: jan@swi.psy.uva.nl
  5.  
  6.     Copyright (C) 1996 University of Amsterdam. All rights reserved.
  7. */
  8.  
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <stdarg.h>
  12. #include <assert.h>
  13. #include <errno.h>
  14. #include <signal.h>
  15. #ifdef HAVE_MALLOC_H
  16. #include <malloc.h>
  17. #endif
  18.  
  19. static int debuglevel = 0;
  20.  
  21. #define DEBUG(n, g)    if ( n <= debuglevel ) { g; }
  22.  
  23. #define MAXFUNC        100        /* max bounded function name length */
  24. #define MAXCMD        256        /* max length of a \foobar */
  25. #define MAXARG        4096        /* max {..} argument length */
  26. #define MAXWORD        1024        /* max length of a word (no spaces) */
  27. #define MAXVERB        1024        /* max length of \verb|string| */
  28. #define MAXMATH        4096        /* max length of $...$ */
  29. #define MAXVERBATIM    10240        /* max size of verbatim */
  30. #define MAXOUTPUT    11000        /* output() max */
  31. #define MAXCMDARGS    32
  32. #define MAXENVNESTING    256        /* max depth of environment */
  33.  
  34. static void error(int errno, const char *file, int line); /* handle errors */
  35. static void warn(int errno, const char *file, int line); /* handle errors */
  36.  
  37. #define ERR_UNEXPECTED_EOF    1    /* unexpected end-of-file */
  38. #define ERR_RUNAWAY_ARGUMENT    2    /* runaway argument */
  39. #define ERR_VERBATIM_TOO_LONG    3    /* verbatim env too large */
  40. #define ERR_NOCMD_SPECS        4    /* cannot find command-specs */
  41. #define ERR_BAD_COMMAND_SPEC    5    /* syntax error in command specs */
  42. #define ERR_ENV_NESTING        6    /* environment-stack overflow */
  43. #define ERR_UNDEF_FUNCTION    7    /* undefined function */
  44. #define ERR_BAD_ARG_SPEC    8
  45. #define ERR_BAD_ENV_SPEC    9
  46. #define ERR_RUNAWAY_MATH       10    /* notclosed math env */
  47. #define ERR_BAD_MATH_ENV_CLOSURE 11    /* $$ closed by $ */
  48. #define ERR_ENV_UNDERFLOW      12    /* environment stack-underflow */
  49. #define ERR_CMD_TOO_LONG       13
  50. #define ERR_BAD_DIM           14
  51.  
  52. const char *tex_error_strings[] =
  53. { "No error",
  54.   "Unexpected end of file",
  55.   "Runaway argument",
  56.   "Verbatim environment too long",
  57.   "Cannot find command specification file",
  58.   "Syntax error",
  59.   "Environment-stack overflow",
  60.   "Undefined function",
  61.   "Syntax error in argument definition",
  62.   "Syntax error in environment definition",
  63.   "$: Runaway argument",
  64.   "$$ closed by single $",
  65.   "Too many '}' or \\end{}",
  66.   "Command name too long",
  67.   "Bad dimension",
  68.   NULL                    /* allow for easy enumeration */
  69. };
  70.  
  71. #ifndef FALSE
  72. #define FALSE 0
  73. #define TRUE  1
  74. #endif
  75.  
  76. #define EOS '\0'            /* end-of-string */
  77. #define streq(s1, s2) (strcmp((s1), (s2)) == 0)
  78.  
  79.          /*******************************
  80.          *         COMMANDS        *
  81.          *******************************/
  82.  
  83. #define F_NOSKIPBLANK    0x01        /* Argument handling */
  84.  
  85. #define CA_OPTIONAL    0x01        /* [optional arg] */
  86. #define CA_TEXT        0x02        /* Argument contains output text */
  87. #define CA_DIM        0x04        /* Argument is a dimension */
  88.  
  89. #define CMD_MODIFY    0x01        /* command allows for modifier */
  90.  
  91. #define PRE_COMMENT    -1        /* put %\n before command */
  92.  
  93. typedef struct _command *Command;
  94. typedef struct _environment *Environment;
  95. typedef struct _token *Token;
  96. typedef struct _input *Input;
  97. typedef struct _output *Output;
  98.  
  99. typedef void (*CallBack)(Token token, void *context);
  100. typedef void (*CmdFunc)(Command cmd, Input fd, CallBack func, void *ctx);
  101. typedef void (*EnvFunc)(Environment cmd, Input fd, CallBack func, void *ctx);
  102. typedef void *AnyFunc;
  103. static AnyFunc lookupFunction(const char *name);
  104.  
  105. typedef struct
  106. { int flags;                /* command arguments flags */
  107. } cmd_arg, *CmdArg;
  108.  
  109. typedef struct _cmd_descr
  110. { const char*name;            /* name of the command */
  111.   int         flags;            /* command-flags */
  112.   CmdArg     arguments;            /* argument specifiers */
  113.   char         arg_count;            /* # arguments */
  114.   char         pre_lines;            /* # newlines needed before */
  115.   char         post_lines;        /* # newlines needed after */
  116.   CmdFunc    function;            /* associated function */
  117.   const char*fname;            /* function-name */
  118.   struct _cmd_descr *next;        /* next in hash-table */
  119. } cmd_descr, *CmdDescr;
  120.  
  121. typedef struct _command
  122. { CmdDescr   command;            /* the commands */
  123.   int         flags;            /* general flags */
  124.   char **    arguments;            /* the actual arguments */
  125. } command;
  126.  
  127. typedef struct _env_descr
  128. { const char *name;            /* name of the environment */
  129.   int          flags;            /* environment flags */
  130.   CmdArg      arguments;        /* argument-list */
  131.   char          arg_count;        /* # arguments */
  132.   EnvFunc     function;            /* associated function */
  133.   const char *fname;            /* function-name */
  134.   struct _env_descr *next;
  135. } env_descr, *EnvDescr;
  136.  
  137. typedef struct _environment
  138. { EnvDescr    environment;
  139.   int          flags;
  140.   char **     arguments;
  141. } environment;
  142.  
  143. #define INPUT_FILE    0        /* reading from a file */
  144. #define INPUT_STRING    1        /* reading from a string */
  145.  
  146. typedef struct _input
  147. { int        type;
  148.   int        lineno;
  149.   const char *  name;            /* name (for feedback) */
  150.   Input        parent;            /* Parent input */
  151.   union
  152.   { FILE       *fd;
  153.     const char *string;
  154.   } stream;
  155. } input;
  156.  
  157. static Input    curin;            /* current input (file) */
  158. static Output    curout;            /* current output */
  159.  
  160.  
  161. static void cmd_prolog(Command g, Input fd, CallBack func, void *ctx);
  162.  
  163.          /*******************************
  164.          *           TOKENS        *
  165.          *******************************/
  166.  
  167.  
  168. #define TOK_CMD         0        /* \cmd */
  169. #define TOK_BEGIN_GROUP     1        /* { */
  170. #define TOK_END_GROUP     2        /* } */
  171. #define TOK_BEGIN_ENV     3        /* \begin{cmd} */
  172. #define TOK_END_ENV     4        /* \end{cmd} */
  173. #define TOK_VERB     5        /* \verb|foo| */
  174. #define TOK_VERBATIM     6        /* verbatim environment */
  175. #define TOK_MATH     7        /* $...$ */
  176. #define TOK_MATH_ENV     8        /* $$...$$ */
  177. #define TOK_PAR         9        /* implicit paragraph (blank line) */
  178. #define TOK_WORD        10        /* general word */
  179. #define TOK_NOSPACEWORD 11        /* word without reintroducing spaces */
  180. #define TOK_SPACE       12        /* blank space */
  181. #define TOK_LINE        13        /* single line */
  182. #define TOK_EOF            14        /* end-of-file */
  183.  
  184. typedef struct _token
  185. { int    type;                /* type identifier */
  186.   int    prelines;            /* HTML stuff */
  187.   int    postlines;            /* HTML stuff */
  188.   char *context;            /* additional context info */
  189.   union 
  190.   { char *string;            /* related text */
  191.     Command cmd;            /* related TeX command */
  192.     Environment env;            /* related TeX environment */
  193.   } value;
  194. } token;
  195.  
  196. static const char *texarg;        /* argument for runaway message */
  197.  
  198.          /*******************************
  199.          *   CHARACTER CLASSIFICATION    *
  200.          *******************************/
  201.  
  202. #define EF 0                /* end-of-file */
  203. #define SP 1                /* space */
  204. #define SC 2                /* start line comment (%) */
  205. #define BG 3                /* begin group ({) */
  206. #define EG 4                /* end group (}) */
  207. #define MM 5                /* math-mode delimiter ($) */
  208. #define TD 6                /* Table delimiter (&) */
  209. #define NB 7                /* Non-breaking space (~) */
  210. #define CM 8                /* command (\) */
  211.  
  212. #define PU 9                /* punctuation */
  213. #define DI 10                /* digit */
  214.  
  215. #define LC 11                /* lower-case letter */
  216. #define UC 12                /* uppercase letter */
  217.  
  218. #define CharType(c) (char_type[(c)+1])
  219.  
  220. #define isspace(c)     (CharType(c) == SP)
  221. #define isdigit(c)     (CharType(c) == DI)
  222. #define isalnum(c)     (CharType(c) >= LC)
  223. #define wbreak(c)    (CharType(c) <= CM) /* breaks a word */
  224. #define isbegingroup(c) (CharType(c) == BG)
  225. #define iscommand(c)    (CharType(c) == CM)
  226.  
  227. static char char_type[] = {
  228. /* EOF */
  229.    EF,
  230. /* ^@  ^A  ^B  ^C  ^D  ^E  ^F  ^G  ^H  ^I  ^J  ^K  ^L  ^M  ^N  ^O    0-15 */
  231.    EF, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, 
  232. /* ^P  ^Q  ^R  ^S  ^T  ^U  ^V  ^W  ^X  ^Y  ^Z  ^[  ^\  ^]  ^^  ^_   16-31 */
  233.    SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, SP, 
  234. /* sp   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   32-47 */
  235.    SP, PU, PU, PU, MM, SC, TD, PU, PU, PU, PU, PU, PU, PU, PU, PU, 
  236. /*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   48-63 */
  237.    DI, DI, DI, DI, DI, DI, DI, DI, DI, DI, PU, PU, PU, PU, PU, PU, 
  238. /*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   64-79 */
  239.    PU, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, 
  240. /*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   80-95 */
  241.    UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, PU, CM, PU, PU, UC, 
  242. /*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   96-111 */
  243.    PU, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, 
  244. /*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  ^?   112-127 */
  245.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, BG, PU, EG, NB, SP, 
  246.               /* 128-255 */
  247.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  248.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  249.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  250.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  251.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  252.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  253.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC,
  254.    LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC, LC
  255. };
  256.  
  257.  
  258.          /*******************************
  259.          *          INPUT        *
  260.          *******************************/
  261.  
  262. static Input
  263. openInputFile(const char *name)
  264. { FILE *fd;
  265.  
  266.   if ( (fd = fopen(name, "r")) )
  267.   { Input i = malloc(sizeof(input));
  268.  
  269.     if ( i )
  270.     { i->type      = INPUT_FILE;
  271.       i->stream.fd = fd;
  272.       i->lineno    = 1;
  273.  
  274.       i->name   = name;
  275.       i->parent = curin;
  276.       curin = i;
  277.  
  278.       return i;
  279.     }
  280.   }
  281.  
  282.   return NULL;
  283. }
  284.  
  285.  
  286. static Input
  287. openInputString(const char *str)
  288. { Input i = malloc(sizeof(input));
  289.  
  290.   if ( i )
  291.   { i->type         = INPUT_STRING;
  292.     i->stream.string = str;
  293.     i->lineno    = 1;
  294.  
  295.     i->name = str;
  296.     i->parent = curin;
  297.     curin = i;
  298.  
  299.     return i;
  300.   }
  301.  
  302.   return NULL;
  303. }
  304.  
  305.  
  306. static void
  307. closeInput(Input i)
  308. { if ( i->type == INPUT_FILE )
  309.     fclose(i->stream.fd);
  310.  
  311.   curin = i->parent;
  312.  
  313.   free(i);
  314. }
  315.  
  316.  
  317. static const char *
  318. texfile()
  319. { if ( curin )
  320.   { Input i = curin;
  321.  
  322.     while(i && i->type == INPUT_STRING)
  323.       i = i->parent;
  324.  
  325.     if ( i && i->type == INPUT_FILE )
  326.       return i->name;
  327.  
  328.     return curin->name;
  329.   }
  330.  
  331.   return "no input";
  332. }
  333.  
  334.  
  335. static int
  336. texline()
  337. { if ( curin )
  338.   { Input i = curin;
  339.     int offset = 0;
  340.  
  341.     while(i && i->type == INPUT_STRING)
  342.     { offset += i->lineno - 1;
  343.       i = i->parent;
  344.     }
  345.  
  346.     if ( i && i->type == INPUT_FILE )
  347.       return i->lineno + offset;
  348.  
  349.     return curin->lineno;
  350.   }
  351.  
  352.   return -1;
  353. }
  354.  
  355.  
  356. static int
  357. mygetc(Input fd)
  358. { int c;
  359.  
  360.   switch(fd->type)
  361.   { case INPUT_FILE:
  362.       c = getc(fd->stream.fd);
  363.       break;
  364.     case INPUT_STRING:
  365.     default:
  366.       c = *fd->stream.string++;
  367.       break;
  368.   }
  369.  
  370.   if ( c == '\n' )
  371.     fd->lineno++;
  372.  
  373.   return c;
  374. }
  375.  
  376. static void
  377. myungetc(int c, Input fd)
  378. { switch(fd->type)
  379.   { case INPUT_FILE:
  380.       ungetc(c, fd->stream.fd);
  381.       break;
  382.     case INPUT_STRING:
  383.     default:
  384.       fd->stream.string--;
  385.       break;
  386.   }
  387.  
  388.   if ( c == '\n' )
  389.     fd->lineno--;
  390. }
  391.  
  392.  
  393. static char *
  394. myfgets(char *buf, int size, Input fd)
  395. { char *s = buf;
  396.  
  397.   for(;;)
  398.   { int    c = mygetc(fd);
  399.     
  400.     if ( c == EOF )
  401.     { if ( s == buf )
  402.     return NULL;
  403.  
  404.       *s = EOS;
  405.       return buf;
  406.     } else if ( c == '\n' )
  407.     { *s++ = c;
  408.       *s = EOS;
  409.       return buf;
  410.     }
  411.  
  412.     *s++ = c;
  413.     if ( s >= &buf[size-1] )
  414.     { *s = EOS;
  415.       return buf;
  416.     }
  417.   }
  418. }
  419.  
  420.  
  421. #undef getc
  422. #undef ungetc
  423. #undef fgets
  424. #define getc(fd) mygetc(fd)
  425. #define ungetc(c, fd) myungetc(c, fd)
  426. #define fgets(buf, size, fd) myfgets(buf, size, fd)
  427.  
  428.          /*******************************
  429.          *          STRINGS        *
  430.          *******************************/
  431.  
  432.  
  433. int
  434. stringHashValue(const char *t, int buckets)
  435. { unsigned int value = 0;
  436.   unsigned int shift = 5;
  437.  
  438.   while(*t)
  439.   { unsigned int c = *t++;
  440.     
  441.     c -= 'a';
  442.     value ^= c << (shift & 0xf);
  443.     shift ^= c;
  444.   }
  445.  
  446.   return (value ^ (value >> 16)) % buckets;
  447. }
  448.  
  449.  
  450. const char *
  451. save_string(const char *s)
  452. { char *cp = malloc(strlen(s)+1);
  453.  
  454.   strcpy(cp, s);
  455.  
  456.   return (const char *)cp;
  457. }
  458.  
  459.  
  460.          /*******************************
  461.          *     COMMAND REGISTRY    *
  462.          *******************************/
  463.  
  464. #define CMD_HASH_SIZE 256
  465.  
  466. static CmdDescr cmd_table[CMD_HASH_SIZE];
  467.  
  468. static CmdDescr
  469. lookupCommand(const char *name)
  470. { int v = stringHashValue(name, CMD_HASH_SIZE);
  471.   CmdDescr c;
  472.  
  473.   for(c = cmd_table[v]; c; c = c->next)
  474.   { if ( streq(c->name, name) )
  475.       return c;
  476.   }
  477.  
  478.   if ( isspace(*name) && name[1] == EOS )
  479.     return lookupCommand(" ");
  480.  
  481.   return NULL;
  482. }
  483.  
  484.  
  485. static CmdDescr
  486. newCommand(const char *name)
  487. { int v = stringHashValue(name, CMD_HASH_SIZE);
  488.   CmdDescr c;
  489.  
  490.   for(c = cmd_table[v]; c; c = c->next)
  491.   { if ( streq(c->name, name) )
  492.     { c->flags = 0;
  493.       c->arg_count = 0;
  494.       if ( c->arguments )
  495.       { free(c->arguments);
  496.     c->arguments = NULL;
  497.       }
  498.       return c;
  499.     }
  500.   }
  501.  
  502.   c = malloc(sizeof(*c));
  503.   c->name       = save_string(name);
  504.   c->flags      = 0;
  505.   c->arg_count  = 0;
  506.   c->arguments  = NULL;
  507.   c->pre_lines  = 0;
  508.   c->post_lines = 0;
  509.   c->function   = NULL;
  510.   c->fname      = NULL;
  511.   c->next       = cmd_table[v];
  512.   cmd_table[v]  = c;
  513.  
  514.   return c;
  515. }
  516.  
  517. #define ENV_HASH_SIZE 256
  518.  
  519. static EnvDescr env_table[ENV_HASH_SIZE];
  520.  
  521. static EnvDescr
  522. lookupEnvironment(const char *name)
  523. { int v = stringHashValue(name, ENV_HASH_SIZE);
  524.   EnvDescr e;
  525.  
  526.   for(e = env_table[v]; e; e = e->next)
  527.   { if ( streq(e->name, name) )
  528.       return e;
  529.   }
  530.  
  531.   return NULL;
  532. }
  533.  
  534.  
  535. static EnvDescr
  536. newEnvironment(const char *name)
  537. { int v = stringHashValue(name, ENV_HASH_SIZE);
  538.   EnvDescr e;
  539.  
  540.   for(e = env_table[v]; e; e = e->next)
  541.   { if ( streq(e->name, name) )
  542.     { e->flags = 0;
  543.       e->arg_count = 0;
  544.       if ( e->arguments )
  545.       { free(e->arguments);
  546.     e->arguments = NULL;
  547.       }
  548.       return e;
  549.     }
  550.   }
  551.  
  552.   e = malloc(sizeof(*e));
  553.   e->name       = save_string(name);
  554.   e->flags      = 0;
  555.   e->arg_count  = 0;
  556.   e->arguments  = NULL;
  557.   e->function   = NULL;
  558.   e->fname      = NULL;
  559.   e->next       = env_table[v];
  560.   env_table[v]  = e;
  561.  
  562.   return e;
  563. }
  564.  
  565. #define skipBanks(s)    while(isspace(*s)) s++
  566.  
  567. static int
  568. parseArgSpec(const char *fname, int lineno, char **line, CmdArg args)
  569. { int nargs = 0;
  570.   char *s = *line;
  571.  
  572.   skipBanks(s);
  573.  
  574.   for(;;s++)
  575.   { switch(*s)
  576.     { case '[':
  577.     args[nargs].flags = 0;
  578.         if ( s[2] != ']' )
  579.     { warn(ERR_BAD_ARG_SPEC, fname, lineno);
  580.       return -1;
  581.     }
  582.     args[nargs].flags |= CA_OPTIONAL;
  583.     goto arg_cont;
  584.       case '{':
  585.     args[nargs].flags = 0;
  586.         if ( s[2] != '}' )
  587.     { warn(ERR_BAD_ARG_SPEC, fname, lineno);
  588.       return -1;
  589.     }
  590.       arg_cont:
  591.     switch(s[1])
  592.     { case '+':
  593.         args[nargs].flags |= CA_TEXT;
  594.       case 'd':
  595.         args[nargs].flags |= CA_DIM;
  596.       case '-':
  597.         break;
  598.       default:
  599.         warn(ERR_BAD_ARG_SPEC, fname, lineno);
  600.         return -1;
  601.     }
  602.         nargs++;
  603.         s += 2;
  604.         break;
  605.       default:
  606.     skipBanks(s);
  607.         *line = s;
  608.         return nargs;
  609.     }
  610.   }
  611. }
  612.  
  613.  
  614. static AnyFunc
  615. parseFuncSpec(char **line, const char **fname)
  616. { char *s = *line;
  617.   char b[MAXFUNC];
  618.   char *q = b;
  619.  
  620.   skipBanks(s);
  621.   if ( *s != '=' )
  622.     return;
  623.   s++;
  624.   while(isalnum(*s))
  625.     *q++ = *s++;
  626.   *q = EOS;
  627.   skipBanks(s);
  628.  
  629.   *line = s;
  630.   *fname = (const char *)save_string(b);
  631.  
  632.   return lookupFunction(b);
  633. }
  634.  
  635.  
  636. static int
  637. parseEnvSpec(const char *fname, int line, char *s)
  638. { char *f = ++s;
  639.   char tmp;
  640.   EnvDescr e;
  641.   cmd_arg args[MAXCMDARGS];        /* argument-list */
  642.   
  643.   while(isalnum(*s))
  644.     s++;
  645.   tmp = *s;
  646.   *s = EOS;
  647.   e = newEnvironment(f);
  648.   *s = tmp;
  649.   
  650.   if ( *s == '*' )            /* \begin{figure*} */
  651.   { e->flags |= CMD_MODIFY;
  652.     s++;
  653.   }
  654.  
  655.   skipBanks(s);
  656.   if ( *s != '}' )            /* check for } */
  657.   { warn(ERR_BAD_ENV_SPEC, fname, line);
  658.     return FALSE;
  659.   } else
  660.     s++;
  661.                     /* parse arguments */
  662.   e->arg_count = parseArgSpec(fname, line, &s, args);
  663.   if ( e->arg_count < 0 )
  664.     return FALSE;
  665.   e->arguments = malloc(e->arg_count*sizeof(cmd_arg));
  666.   memcpy(e->arguments, args, e->arg_count*sizeof(cmd_arg));
  667.  
  668.   if ( *s == '=' )            /* =function */
  669.   { e->function = parseFuncSpec(&s, &e->fname);
  670.     skipBanks(s);
  671.   }
  672.  
  673.   if ( *s != EOS && *s != '%' )
  674.   { warn(ERR_BAD_COMMAND_SPEC, fname, line);
  675.     return FALSE;
  676.   }
  677.  
  678.   return TRUE;
  679. }
  680.  
  681.  
  682. static int
  683. parseCommandSpec(const char *fname, int lineno, char *line)
  684. { char *s = line;
  685.  
  686.   while(isspace(*s))
  687.     s++;
  688.  
  689.   if ( *s == '%' || *s == EOS )
  690.     return TRUE;            /* comment-line */
  691.  
  692.   if ( *s == '{' )            /* environment */
  693.   { return parseEnvSpec(fname, lineno, s);
  694.   } else if ( *s == '\\' )        /* normal command */
  695.   { char *f = ++s;
  696.     char tmp;
  697.     cmd_arg args[MAXCMDARGS];        /* argument-list */
  698.     int nargs = 0;
  699.     CmdDescr c;
  700.  
  701.     if ( isalnum(*s) )
  702.     { while(isalnum(*s))
  703.       { s++;
  704.       }
  705.     } else
  706.       s++;
  707.     tmp = *s;
  708.     *s = EOS;
  709.     c = newCommand(f);
  710.     *s = tmp;
  711.  
  712.     skipBanks(s);            /* spaces after command */
  713.     if ( *s == '*' )            /* modified */
  714.     { c->flags |= CMD_MODIFY;
  715.       s++;
  716.     }
  717.  
  718.     c->arg_count = parseArgSpec(fname, lineno, &s, args);
  719.     if ( c->arg_count < 0 )
  720.       return FALSE;
  721.     c->arguments = malloc(c->arg_count*sizeof(cmd_arg));
  722.     memcpy(c->arguments, args, c->arg_count*sizeof(cmd_arg));
  723.  
  724.     if ( *s == '=' )        /* associate function */
  725.     { c->function = parseFuncSpec(&s, &c->fname);
  726.     }
  727.  
  728.     if ( isdigit(*s) )        /* pre-lines */
  729.     { c->pre_lines = *s - '0';
  730.       s++;
  731.       skipBanks(s);
  732.     } else if ( *s == '%' )
  733.     { c->pre_lines = PRE_COMMENT;    /* %\n */
  734.       s++;
  735.       skipBanks(s);
  736.     }
  737.  
  738.     if ( isdigit(*s) )        /* post-lines */
  739.     { c->post_lines = *s - '0';
  740.       s++;
  741.       skipBanks(s);
  742.     }
  743.  
  744.     if ( *s != EOS && *s != '%' )
  745.     { warn(ERR_BAD_COMMAND_SPEC, fname, lineno);
  746.       return FALSE;
  747.     }
  748.  
  749.     return TRUE;
  750.   }
  751.  
  752.   warn(ERR_BAD_COMMAND_SPEC, fname, lineno);
  753.   return FALSE;
  754. }
  755.  
  756.  
  757. static int
  758. parseCmdSpecs(const char *fname)
  759. { char line[MAXCMD];
  760.   int l = 0;
  761.   Input fd;
  762.  
  763.   if ( (fd = openInputFile(fname)) == NULL )
  764.   { warn(ERR_NOCMD_SPECS, fname, 0);
  765.     return FALSE;
  766.   }
  767.  
  768.   while(fgets(line, sizeof(line), fd))
  769.     parseCommandSpec(fname, ++l, line);
  770.   
  771.   closeInput(fd);
  772.  
  773.   return TRUE;
  774. }
  775.  
  776.  
  777.          /*******************************
  778.          *       PARSING STUFF    *
  779.          *******************************/
  780.  
  781. static void
  782. getCommand(Input fd, char *buf, int size)
  783. { int c;
  784.  
  785.   size--;                /* room for EOS */
  786.  
  787.   c = getc(fd);
  788.   if ( isalnum(c) )            /* \blabla */
  789.   { do
  790.     { if ( --size <= 0 )
  791.     error(ERR_CMD_TOO_LONG, texfile(), texline());
  792.       *buf++ = c;
  793.       c = getc(fd);
  794.     } while(isalnum(c));
  795.     ungetc(c, fd);
  796.   } else                /* \" */
  797.   { *buf++ = c;
  798.   }
  799.  
  800.   *buf = EOS;
  801. }
  802.  
  803.  
  804. static void
  805. getArgument(Input fd, int flags, char *buf, int size)
  806. { int c = getc(fd);
  807.   int sz = size;
  808.  
  809.   if ( !(flags & F_NOSKIPBLANK) )
  810.   { while(isspace(c))
  811.       c = getc(fd);
  812.   }
  813.  
  814.   if ( isbegingroup(c) )        /* { */
  815.   { int nesting = 1; char *s = buf;
  816.     int prev = c;
  817.     
  818.     for(;;)
  819.     { c = getc(fd);
  820.  
  821.       switch(CharType(c))
  822.       { case CM:
  823.       *s++ = c;
  824.       continue;
  825.     case BG:
  826.       nesting++;
  827.       break;
  828.     case EG:
  829.       nesting--;
  830.       break;
  831.     case SP:
  832.       while(isspace(c))
  833.         c = getc(fd);
  834.       ungetc(c, fd);
  835.       c = ' ';
  836.       break;
  837.     case EF:
  838.       error(ERR_UNEXPECTED_EOF, texfile(), texline());
  839.       }
  840.  
  841.       if ( nesting > 0 )
  842.       { if ( --sz < 0 )
  843.     { buf[size-1] = EOS;
  844.       texarg = buf;
  845.       error(ERR_RUNAWAY_ARGUMENT, texfile(), texline());
  846.     }
  847.     *s++ = c;
  848.     prev = c;
  849.       } else
  850.     break;
  851.     }
  852.  
  853.     *s++ = EOS;
  854.   } else if ( iscommand(c) )        /* \ */
  855.   { *buf++ = c;
  856.     size--;
  857.     getCommand(fd, buf, size);
  858.   } else
  859.   { *buf++ = c;
  860.     *buf = EOS;
  861.   }
  862. }
  863.  
  864.  
  865. static int
  866. getOptionalArgument(Input fd, int flags, char *buf, int size)
  867. { int c = getc(fd);
  868.   int sz = size;
  869.  
  870.   if ( c == '[' )
  871.   { int nesting = 1; char *s = buf;
  872.     int prev = c;
  873.  
  874.     for(;;)
  875.     { c = getc(fd);
  876.  
  877.       switch(CharType(c))
  878.       { case CM:
  879.       *s++ = c;
  880.       continue;
  881.     case BG:
  882.       nesting++;
  883.       break;
  884.     case EG:
  885.       nesting--;
  886.       break;
  887.     case EF:
  888.       error(ERR_UNEXPECTED_EOF, texfile(), texline());
  889.     default:
  890.       switch(c)
  891.       { case '[':
  892.           nesting++;
  893.           break;
  894.         case ']':
  895.           nesting--;
  896.           break;
  897.       }
  898.       }
  899.  
  900.       if ( c != ']' || nesting > 0 )
  901.       { if ( --sz < 0 )
  902.     { buf[size-1] = EOS;
  903.       texarg = buf;
  904.       error(ERR_RUNAWAY_ARGUMENT, texfile(), texline());
  905.     }
  906.     *s++ = c;
  907.     prev = c;
  908.       } else
  909.     break;
  910.     }
  911.  
  912.     *s++ = EOS;
  913.     return TRUE;
  914.   } else
  915.     ungetc(c, fd);
  916.  
  917.   return FALSE;
  918. }
  919.  
  920.  
  921. static void
  922. getDimension(Input fd, int flags, char *buf, int size)
  923. { int c = getc(fd);
  924.   int sz = size;
  925.  
  926.   if ( !(flags & F_NOSKIPBLANK) )
  927.   { while(isspace(c))
  928.       c = getc(fd);
  929.   }
  930.  
  931.   if ( isbegingroup(c) )
  932.   { ungetc(c, fd);
  933.  
  934.     getArgument(fd, flags, buf, size);
  935.   } else if ( iscommand(c) )
  936.   { buf[0] = c;
  937.     getCommand(fd, &buf[1], size-1);
  938.   } else if ( isdigit(c) )
  939.   { char *s = buf;
  940.     do
  941.     { *s++ = c;
  942.       c = getc(fd);
  943.     } while(isdigit(c) || c == '.' );
  944.     if ( isalnum(c) )
  945.     { *s++ = c;
  946.       c = getc(fd);
  947.     }
  948.     if ( isalnum(c) )
  949.     { *s++ = c;
  950.       *s = EOS;
  951.       return;
  952.     }
  953.  
  954.     error(ERR_BAD_DIM, texfile(), texline());
  955.   }
  956. }
  957.  
  958.  
  959. static void
  960. parseCommand(Input fd, const char *name, CallBack func, void *ctx)
  961. { CmdDescr cmd = lookupCommand(&name[1]); /* skip \ */
  962.   command g;
  963.   token t;
  964.   int n, c;
  965.   int flags = 0;
  966.  
  967.   if ( !cmd )
  968.   { fprintf(stderr, "[WARNING: Unknown command: %s]\n", name);
  969.     cmd = newCommand(&name[1]);
  970.   }
  971.  
  972.   g.command = cmd;
  973.   g.flags   = 0;
  974.  
  975.   if ( cmd->name[1] == EOS && !isalnum(cmd->name[0]) )
  976.     flags |= F_NOSKIPBLANK;
  977.  
  978.   c = getc(fd);
  979.   if ( cmd->arg_count > 0 )
  980.     g.arguments = alloca(sizeof(char *) * cmd->arg_count);
  981.   else
  982.     g.arguments = NULL;
  983.  
  984.   if ( !(flags & F_NOSKIPBLANK) )
  985.   { while(isspace(c))
  986.       c = getc(fd);
  987.   }
  988.  
  989.   if ( cmd->flags & CMD_MODIFY && c == '*' ) /* \section* (modified) */
  990.   { g.flags |= CMD_MODIFY;
  991.     c = getc(fd);
  992.     if ( !(flags & F_NOSKIPBLANK) )
  993.     { while(isspace(c))
  994.     c = getc(fd);
  995.     }
  996.   }
  997.   ungetc(c, fd);
  998.  
  999.   for(n=0; n<cmd->arg_count; n++)    /* process the arguments */
  1000.   { char abuf[MAXARG];
  1001.  
  1002.     if ( cmd->arguments[n].flags & CA_OPTIONAL )
  1003.     { if ( getOptionalArgument(fd, flags, abuf, sizeof(abuf)) )
  1004.       { g.arguments[n] = alloca(strlen(abuf)+1);
  1005.     strcpy(g.arguments[n], abuf);
  1006.       } else
  1007.     g.arguments[n] = NULL;
  1008.     } else if ( cmd->arguments[n].flags & CA_DIM )
  1009.     { getDimension(fd, flags, abuf, sizeof(abuf));
  1010.       g.arguments[n] = alloca(strlen(abuf)+1);
  1011.       strcpy(g.arguments[n], abuf);
  1012.     } else
  1013.     { getArgument(fd, flags, abuf, sizeof(abuf));
  1014.       g.arguments[n] = alloca(strlen(abuf)+1);
  1015.       strcpy(g.arguments[n], abuf);
  1016.     }
  1017.   }
  1018.  
  1019.   if ( cmd->function )
  1020.   { (*cmd->function)(&g, fd, func, ctx);
  1021.   } else
  1022.   { t.type = TOK_CMD;
  1023.     t.value.cmd = &g;
  1024.     (*func)(&t, ctx);
  1025.   }
  1026. }
  1027.  
  1028. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1029. Handle verbatim environment
  1030. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1031.  
  1032. static void
  1033. env_verbatim(Environment e, Input fd, CallBack func, void *ctx)
  1034. { char end[MAXCMD];
  1035.   char buf[MAXVERBATIM];
  1036.   int left = MAXVERBATIM-1;
  1037.   char *s = buf;
  1038.   int el;
  1039.   token t;
  1040.  
  1041.   sprintf(end, "\\end{%s}", e->environment->name);
  1042.   el = strlen(end);
  1043.   
  1044.   for(;;)
  1045.   { if ( --left == 0 )
  1046.       error(ERR_VERBATIM_TOO_LONG, texfile(), texline());
  1047.     *s++ = getc(fd);
  1048.  
  1049.     if ( s[-el] == '\\' && strncmp(&s[-el], end, el) == 0 )
  1050.     { s[-el] = EOS;
  1051.       t.type = TOK_VERBATIM;
  1052.       t.context = (char *)e->environment->name;
  1053.       t.value.string = buf;
  1054.       (*func)(&t, ctx);
  1055.       return;
  1056.     }
  1057.   }
  1058. }
  1059.  
  1060.  
  1061. static void
  1062. env_normal(Environment e, Input fd, CallBack func, void *ctx)
  1063. { token t;
  1064.  
  1065.   t.type = TOK_BEGIN_ENV;
  1066.   t.value.env = e;
  1067.   (*func)(&t, ctx);
  1068. }
  1069.  
  1070.  
  1071. static void
  1072. cmd_normal(Command g, Input fd, CallBack func, void *ctx)
  1073. { token t;
  1074.  
  1075.   t.type = TOK_CMD;
  1076.   t.value.cmd = g;
  1077.   (*func)(&t, ctx);
  1078. }
  1079.  
  1080.  
  1081. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1082. handle \begin command
  1083. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1084.  
  1085. static void
  1086. cmd_begin(Command g, Input fd, CallBack func, void *ctx)
  1087. { char ename[MAXCMD];
  1088.   int enl, n;
  1089.   char *args[MAXCMDARGS];
  1090.   environment e;
  1091.   EnvDescr env;
  1092.   token t;
  1093.  
  1094.   e.flags = 0;
  1095.   e.arguments = args;
  1096.   getArgument(fd, 0, ename, sizeof(ename));
  1097.   enl = strlen(ename);
  1098.  
  1099.   if ( ename[enl-1] == '*' )        /* check for modified env */
  1100.   { ename[enl-1] = EOS;
  1101.     e.flags |= CMD_MODIFY;
  1102.   }
  1103.  
  1104.                     /* find the environment */
  1105.   if ( !(env = lookupEnvironment(ename)) )
  1106.   { fprintf(stderr, "WARNING: undefined environment: %s\n", ename);
  1107.     env = newEnvironment(ename);
  1108.   }
  1109.   e.environment = env;
  1110.  
  1111.   for(n=0; n<env->arg_count; n++)    /* process the arguments */
  1112.   { char abuf[MAXARG];
  1113.  
  1114.     if ( env->arguments[n].flags & CA_OPTIONAL )
  1115.     { if ( getOptionalArgument(fd, 0, abuf, sizeof(abuf)) )
  1116.       { e.arguments[n] = alloca(strlen(abuf)+1);
  1117.         strcpy(e.arguments[n], abuf);
  1118.       } else
  1119.     e.arguments[n] = NULL;
  1120.     } else
  1121.     { getArgument(fd, 0, abuf, sizeof(abuf));
  1122.       e.arguments[n] = alloca(strlen(abuf)+1);
  1123.       strcpy(e.arguments[n], abuf);
  1124.     }
  1125.   }
  1126.  
  1127.   if ( env->function )
  1128.   { (*env->function)(&e, fd, func, ctx);
  1129.   } else
  1130.   { t.type = TOK_BEGIN_ENV;
  1131.     t.value.env = &e;
  1132.     (*func)(&t, ctx);
  1133.   }
  1134. }
  1135.  
  1136.  
  1137. static void
  1138. cmd_end(Command g, Input fd, CallBack func, void *ctx)
  1139. { token t;
  1140.  
  1141.   t.type = TOK_END_ENV;
  1142.   t.value.string = g->arguments[0];    /* name of the environment */
  1143.   (*func)(&t, ctx);
  1144. }
  1145.  
  1146.  
  1147. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  1148. handle \verb command
  1149. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  1150.  
  1151. void
  1152. cmd_verb(Command g, Input fd, CallBack func, void *ctx)
  1153. { char buf[MAXVERB];
  1154.   char *s = buf;
  1155.   int delim = getc(fd);
  1156.   int c = getc(fd);
  1157.   token t;
  1158.   char ds[2];
  1159.  
  1160.   ds[0] = delim;
  1161.   ds[1] = EOS;
  1162.   while(c != delim && c != EOF)
  1163.   { *s++ = c;
  1164.     c = getc(fd);
  1165.   }
  1166.   if ( c == EOF )
  1167.     error(ERR_UNEXPECTED_EOF, texfile(), texline());
  1168.   *s++ = EOS;
  1169.  
  1170.   t.type = TOK_VERB;
  1171.   t.context = ds;
  1172.   t.value.string  = buf;
  1173.   (*func)(&t, ctx);
  1174. }
  1175.  
  1176. #define ACTIVE(n, f) { n, f }
  1177.  
  1178. typedef struct
  1179. { char      *name;            /* name */
  1180.   AnyFunc  function;            /* associated function */
  1181. } active, *Active;
  1182.  
  1183. static active active_list[] =
  1184. { ACTIVE("verb",    cmd_verb),
  1185.   ACTIVE("begin",    cmd_begin),
  1186.   ACTIVE("end",        cmd_end),
  1187.   ACTIVE("item",    cmd_normal),
  1188.   ACTIVE("prolog",    cmd_prolog),
  1189.   ACTIVE("verbatim",    env_verbatim),
  1190.   ACTIVE("list",    env_normal),
  1191.   ACTIVE("float",    env_normal),
  1192.   ACTIVE(NULL,        NULL)
  1193. };
  1194.  
  1195. static AnyFunc
  1196. lookupFunction(const char *name)
  1197. { Active a = active_list;
  1198.  
  1199.   for( ; a->name; a++ )
  1200.   { if ( streq(name, a->name) )
  1201.       return a->function;
  1202.   }
  1203.  
  1204.   error(ERR_UNDEF_FUNCTION, texfile(), texline());
  1205.   return NULL;
  1206. }
  1207.  
  1208.  
  1209. static void
  1210. parseMath(Input fd, CallBack func, void *ctx)
  1211. { int c = getc(fd);
  1212.   char buf[MAXMATH];
  1213.   char *s = buf;
  1214.   token t;
  1215.   int nesting = 0;
  1216.   int left = MAXMATH-1;
  1217.  
  1218.   if ( CharType(c) == MM )        /* $$ */
  1219.   { t.type = TOK_MATH_ENV;
  1220.   } else
  1221.   { t.type = TOK_MATH;
  1222.     ungetc(c, fd);
  1223.   }
  1224.  
  1225.   for(;;)
  1226.   { c = getc(fd);
  1227.  
  1228.     switch(CharType(c))
  1229.     { case BG:
  1230.     nesting++;
  1231.     break;
  1232.       case EG:
  1233.     nesting--;
  1234.     break;
  1235.       case SP:
  1236.     while(isspace(c))
  1237.       c = getc(fd);
  1238.     ungetc(c, fd);
  1239.     c = ' ';
  1240.     break;
  1241.       case EF:
  1242.     error(ERR_UNEXPECTED_EOF, texfile(), texline());
  1243.     }
  1244.  
  1245.     if ( c != '$' || nesting > 0 )
  1246.     { if ( --left < 0 )
  1247.       { texarg = buf;
  1248.     error(ERR_RUNAWAY_MATH, texfile(), texline());
  1249.       }
  1250.       *s++ = c;
  1251.     } else
  1252.       break;
  1253.   }
  1254.  
  1255.   *s = EOS;
  1256.   if ( t.type == TOK_MATH_ENV )
  1257.   { c = getc(fd);
  1258.     if ( c != '$' )
  1259.       error(ERR_BAD_MATH_ENV_CLOSURE, texfile(), texline());
  1260.   }
  1261.   
  1262.   t.value.string = buf;
  1263.   (*func)(&t, ctx);
  1264. }
  1265.  
  1266.  
  1267. static void
  1268. parseTeX(Input fd, CallBack func, void *ctx)
  1269. { token t;
  1270.  
  1271.   int c = getc(fd);
  1272.  
  1273.   for(;;)
  1274.   { switch(CharType(c))
  1275.     { case SP:                /* blank space */
  1276.       { int lines = 0;
  1277.   
  1278.     do
  1279.     { if ( c == '\n' )
  1280.         lines++;
  1281.       c = getc(fd);
  1282.     } while(isspace(c));
  1283.   
  1284.     if ( lines >= 2 )
  1285.     { t.type = TOK_PAR;
  1286.       t.value.string = NULL;
  1287.  
  1288.       (*func)(&t, ctx);
  1289.     } else if ( lines == 1 )
  1290.     { t.type = TOK_LINE;
  1291.       t.value.string = NULL;
  1292.  
  1293.       (*func)(&t, ctx);
  1294.     } else
  1295.     { t.type = TOK_SPACE;
  1296.       t.value.string = NULL;
  1297.  
  1298.       (*func)(&t, ctx);
  1299.     }
  1300.  
  1301.     break;
  1302.       }
  1303.       case BG:
  1304.       { char buf[2];
  1305.     
  1306.     buf[0] = c;
  1307.     buf[1] = EOS;
  1308.  
  1309.     t.type = TOK_BEGIN_GROUP;
  1310.     t.value.string = buf;
  1311.     (*func)(&t, ctx);
  1312.     c = getc(fd);
  1313.  
  1314.     break;
  1315.       }
  1316.       case EG:
  1317.       { char buf[2];
  1318.     
  1319.     buf[0] = c;
  1320.     buf[1] = EOS;
  1321.  
  1322.     t.type = TOK_END_GROUP;
  1323.     t.value.string = buf;
  1324.     (*func)(&t, ctx); c = getc(fd);
  1325.  
  1326.     break;
  1327.       }
  1328.       case CM:                /* \command */
  1329.       { char buf[MAXCMD];
  1330.  
  1331.     buf[0] = c;
  1332.     getCommand(fd, &buf[1], MAXCMD-1);
  1333.  
  1334.     parseCommand(fd, buf, func, ctx);
  1335.     c = getc(fd);
  1336.  
  1337.     break;
  1338.       }
  1339.       case MM:
  1340.       { parseMath(fd, func, ctx);
  1341.      c = getc(fd);
  1342.  
  1343.         break;
  1344.       }
  1345.       case SC:                /* % comment */
  1346.       { do
  1347.     { c = getc(fd);
  1348.     } while( c != EOF && c != '\n' );
  1349.  
  1350.     while(isspace(c))
  1351.       c = getc(fd);
  1352.  
  1353.     break;
  1354.       }
  1355.       case EF:                /* end-of-file */
  1356.       { t.type = TOK_EOF;
  1357.     t.value.string = NULL;
  1358.     (*func)(&t, ctx);
  1359.  
  1360.     return;
  1361.       }
  1362.       case TD:                /* & */
  1363.       case NB:                /* ~ */
  1364.       { char buf[2];
  1365.  
  1366.     buf[0] = c;
  1367.     buf[1] = EOS;
  1368.     t.type = TOK_WORD;
  1369.     t.value.string = buf;
  1370.     (*func)(&t, ctx);
  1371.     c = getc(fd);
  1372.  
  1373.     break;
  1374.       }
  1375.       default:                /* default: begin a word */
  1376.       { char buf[MAXWORD];
  1377.     char *s = buf;
  1378.  
  1379.     do
  1380.     { *s++ = c;
  1381.       c = getc(fd);
  1382.     } while(!wbreak(c));
  1383.     *s = EOS;
  1384.     t.type = TOK_WORD;
  1385.     t.value.string = buf;
  1386.     (*func)(&t, ctx);
  1387.  
  1388.     break;
  1389.       }
  1390.     }
  1391.   }
  1392. }
  1393.  
  1394.  
  1395.          /*******************************
  1396.              *           MAIN LOOP          *
  1397.          *******************************/
  1398.  
  1399. static int
  1400. parseTeXFile(const char *file, CallBack func, void *ctx)
  1401. { Input fd; int rval;
  1402.  
  1403.   if ( (fd = openInputFile(file)) == NULL )
  1404.   { fprintf(stderr, "Can't open %s: %s", file, strerror(errno));
  1405.  
  1406.     return FALSE;
  1407.   }
  1408.   parseTeX(fd, func, ctx);
  1409.  
  1410.   closeInput(fd);
  1411.  
  1412.   return TRUE;
  1413. }
  1414.  
  1415.  
  1416.  
  1417.          /*******************************
  1418.            *          TEST              *
  1419.          *******************************/
  1420.  
  1421. typedef struct
  1422. { int    envnesting;            /* nesting of begin/end */
  1423.   int   last_type;            /* type of previous token */
  1424.   int    line_pos;            /* position in line */
  1425.   int   newlines;            /* # consecutive newlines */
  1426.   int    spaces;                /* # consecutive spaces */
  1427.   int    verbatim;            /* verbatim output */
  1428.   int   left_margin;            /* left margin for text */
  1429.   int   right_margin;            /* right margin for text */
  1430.   FILE *fd;                /* output descriptor */
  1431. } ppcontext, *PPContext;
  1432.  
  1433.  
  1434. static void
  1435. output(PPContext pp, const char *fmt, ...)
  1436. { va_list args;
  1437.   char buf[MAXOUTPUT];
  1438.   char *s = buf;
  1439.  
  1440.   va_start(args, fmt);
  1441.   vsprintf(buf, fmt, args);
  1442.   va_end(fmt);
  1443.  
  1444.   if ( pp->verbatim )
  1445.   { for(;;s++)
  1446.     { int c;
  1447.   
  1448.       switch((c = *s))
  1449.       { case EOS:
  1450.       return;
  1451.     case '\n':
  1452.       putc(c, pp->fd);
  1453.       pp->line_pos = 0;
  1454.       pp->spaces++;
  1455.       break;
  1456.     case '\t':
  1457.       putc(c, pp->fd);
  1458.       pp->line_pos |= 0x7;
  1459.       pp->line_pos++;
  1460.       pp->spaces++;
  1461.       break;
  1462.     case ' ':
  1463.       pp->spaces++;
  1464.       putc(c, pp->fd);
  1465.       pp->line_pos++;
  1466.       break;
  1467.     default:
  1468.       pp->spaces = 0;
  1469.       putc(c, pp->fd);
  1470.       pp->line_pos++;
  1471.       }
  1472.     }
  1473.   } else
  1474.   { for(;;s++)
  1475.     { int c;
  1476.   
  1477.       switch((c = *s))
  1478.       { case EOS:
  1479.       return;
  1480.     case '\n':
  1481.       if ( ++pp->newlines <= 2 )
  1482.         putc(c, pp->fd);
  1483.       pp->line_pos = 0;
  1484.       pp->spaces = 1;
  1485.       break;
  1486.     case '\t':
  1487.       c = ' ';
  1488.     case ' ':
  1489.       pp->newlines = 0;
  1490.       if ( ++pp->spaces <= 1 )
  1491.       { putc(c, pp->fd);
  1492.         pp->line_pos++;
  1493.       }
  1494.       break;
  1495.     default:
  1496.       pp->newlines = 0;
  1497.       pp->spaces = 0;
  1498.       putc(c, pp->fd);
  1499.       pp->line_pos++;
  1500.       }
  1501.     }
  1502.   }
  1503. }
  1504.  
  1505.  
  1506. static void
  1507. nl(PPContext pp)
  1508. { int spaces = pp->left_margin % 8;
  1509.   int tabs   = pp->left_margin / 8;
  1510.   int n;
  1511.  
  1512.   output(pp, "\n");
  1513.   for(n=0; n<tabs; n++)
  1514.     output(pp, "\t");
  1515.   for(n=0; n<spaces; n++)
  1516.     output(pp, " ");
  1517. }
  1518.  
  1519.  
  1520. void
  1521. outputBlank(PPContext pp)
  1522. { switch(pp->last_type)
  1523.   { case TOK_SPACE:
  1524.       if ( pp->newlines == 0 )
  1525.     output(pp, " ");
  1526.       break;
  1527.     case TOK_LINE:
  1528.       if ( pp->newlines < 1 )
  1529.     output(pp, "\n");
  1530.       break;
  1531.   }
  1532. }
  1533.  
  1534.  
  1535. static char *tok_names[] =
  1536. { "CMD", "BG", "EG", "BE", "EE", "VERB",
  1537.   "VERBATIM", "$", "$$", "PAR", "W", "S", "L", "EOF"
  1538. };
  1539.  
  1540. void
  1541. put_token(Token t, void *ctx)
  1542. { PPContext pp = ctx;
  1543.   static CmdDescr CMD_BEGIN, CMD_END;
  1544.  
  1545.   if ( !CMD_BEGIN )
  1546.   { CMD_BEGIN = lookupCommand("begin");
  1547.     CMD_END   = lookupCommand("end");
  1548.   }
  1549.  
  1550.   DEBUG(1, output(pp, "[%s]", tok_names[t->type]));
  1551.  
  1552.   switch(t->type)
  1553.   { case TOK_CMD:
  1554.     { Command g = t->value.cmd;
  1555.       int n;
  1556.       int args_printed = 0;
  1557.  
  1558.       outputBlank(pp);
  1559.       if ( g->command->pre_lines == PRE_COMMENT )
  1560.       { output(pp, "%\n");
  1561.       } else
  1562.       { while(pp->newlines < g->command->pre_lines)
  1563.       output(pp, "\n");
  1564.       }
  1565.       output(pp, "\\%s", g->command->name);
  1566.       if ( g->flags & CMD_MODIFY )
  1567.     output(pp, "*");
  1568.       for(n=0; n<g->command->arg_count; n++)
  1569.       { if ( g->command->arguments[n].flags & CA_OPTIONAL )
  1570.     { if ( g->arguments[n] )
  1571.       { output(pp, "[%s]", g->arguments[n]);
  1572.         args_printed++;
  1573.       }
  1574.     } else
  1575.     { output(pp, "{%s}", g->arguments[n]);
  1576.       args_printed++;
  1577.     }
  1578.       }
  1579.       if ( !args_printed )
  1580.       { if ( isalnum(g->command->name[strlen(g->command->name)-1]) )
  1581.       output(pp, " ");
  1582.       }
  1583.       while(pp->newlines < g->command->post_lines)
  1584.     output(pp, "\n");
  1585.  
  1586.       break;
  1587.     }
  1588.     case TOK_BEGIN_ENV:
  1589.     { Environment e = t->value.env;
  1590.       int n;
  1591.  
  1592.       outputBlank(pp);
  1593.       while(pp->newlines < CMD_BEGIN->pre_lines)
  1594.     output(pp, "\n");
  1595.       output(pp, "\\begin{%s", e->environment->name);
  1596.       if ( e->flags & CMD_MODIFY )
  1597.     output(pp, "*");
  1598.       output(pp, "}");
  1599.       for(n=0; n<e->environment->arg_count; n++)
  1600.       { if ( e->environment->arguments[n].flags & CA_OPTIONAL )
  1601.     { if ( e->arguments[n] )
  1602.         output(pp, "[%s]", e->arguments[n]);
  1603.     } else
  1604.     { output(pp, "{%s}", e->arguments[n]);
  1605.     }
  1606.       }
  1607.       while(pp->newlines < CMD_BEGIN->post_lines)
  1608.     output(pp, "\n");
  1609.  
  1610.       break;
  1611.     }
  1612.     case TOK_END_ENV:
  1613.     { outputBlank(pp);
  1614.       while(pp->newlines < CMD_END->pre_lines)
  1615.     output(pp, "\n");
  1616.       output(pp, "\\begin{%s}", t->value.string);
  1617.       while(pp->newlines < CMD_END->post_lines)
  1618.     output(pp, "\n");
  1619.  
  1620.       break;
  1621.     }
  1622.     case TOK_BEGIN_GROUP:
  1623.       outputBlank(pp);
  1624.       output(pp, "{");
  1625.       break;
  1626.     case TOK_END_GROUP:
  1627.       outputBlank(pp);
  1628.       output(pp, "}");
  1629.       break;
  1630.     case TOK_MATH:
  1631.       outputBlank(pp);
  1632.       output(pp, "$%s$", t->value.string);
  1633.       break;
  1634.     case TOK_MATH_ENV:
  1635.       outputBlank(pp);
  1636.       output(pp, "$$%s$$", t->value.string);
  1637.       break;
  1638.     case TOK_VERB:
  1639.       outputBlank(pp);
  1640.       pp->verbatim = TRUE;
  1641.       output(pp, "\\verb%s%s%s", t->context, t->value.string, t->context);
  1642.       pp->verbatim = FALSE;
  1643.       break;
  1644.     case TOK_VERBATIM:
  1645.  
  1646.       while( pp->newlines < CMD_BEGIN->pre_lines )
  1647.     output(pp, "\n");
  1648.       output(pp, "\\begin{%s}", t->context);
  1649.       pp->verbatim = TRUE;
  1650.       output(pp, "%s", t->value.string);
  1651.       pp->verbatim = FALSE;
  1652.       output(pp, "\\end{%s}", t->context);
  1653.       while( pp->newlines < CMD_BEGIN->post_lines )
  1654.     output(pp, "\n");
  1655.       break;
  1656.     case TOK_PAR:
  1657.       output(pp, "\n\n");
  1658.       break;
  1659.     case TOK_WORD:
  1660.     { int pendingblank;
  1661.  
  1662.       if ( pp->last_type == TOK_LINE )
  1663.     pp->last_type = TOK_SPACE;
  1664.       pendingblank = (pp->last_type == TOK_SPACE);
  1665.       outputBlank(pp);            /* as space! */
  1666.       if ( pendingblank &&        /* no blanks in input: concatenate! */
  1667.        strlen(t->value.string) + pp->line_pos > pp->right_margin )
  1668.     nl(pp);
  1669.       output(pp, "%s", t->value.string);
  1670.       break;
  1671.     }
  1672.     case TOK_LINE:
  1673.     case TOK_SPACE:
  1674.       break;
  1675.     case TOK_EOF:
  1676.       output(pp, "\n");
  1677.       break;
  1678.     default:
  1679.       assert(0);
  1680.   }
  1681.  
  1682.   pp->last_type = t->type;
  1683. }
  1684.  
  1685. #ifdef TEST
  1686.  
  1687. static void
  1688. error(int errno, const char *file, int line)
  1689. { fprintf(stderr, "ERROR: %s:%d: %s\n", file, line, tex_error_strings[errno]);
  1690.  
  1691.   exit(1);
  1692. }
  1693.  
  1694.  
  1695. int
  1696. main(int argc, char **argv)
  1697. { parseCmdSpecs("cmd.spec");
  1698.  
  1699.   if ( argc == 2 )
  1700.   { ppcontext pp;
  1701.  
  1702.     pp.envnesting   = 0;
  1703.     pp.last_type    = TOK_EOF;
  1704.     pp.line_pos     = 0;
  1705.     pp.newlines     = 0;
  1706.     pp.spaces       = 0;
  1707.     pp.verbatim     = FALSE;
  1708.     pp.left_margin  = 0;
  1709.     pp.right_margin = 72;
  1710.  
  1711.     parseTeXFile(argv[1], put_token, &pp);
  1712.   }
  1713.  
  1714.   exit(0);
  1715. }
  1716.  
  1717. #endif /*TEST*/
  1718.  
  1719. #ifdef __SWI_PROLOG__
  1720.  
  1721.          /*******************************
  1722.          *     PROLOG CONNECTION    *
  1723.          *******************************/
  1724.  
  1725. #include <SWI-Prolog.h>
  1726.  
  1727. static void build_list(Token t, void *context);
  1728.  
  1729. typedef struct
  1730. { term_t list;                /* list we are working on */
  1731.   term_t head;                /* head (tmp term ref) */
  1732.   int    envnesting;            /* depth of stack */
  1733.   int     prev_type0;            /* type of previous token */
  1734.   int     prev_type1;            /* type of token before that */
  1735.   term_t stack[MAXENVNESTING];        /* Pushed environment */
  1736. } pl_context, *PlContext;
  1737.  
  1738.  
  1739. static functor_t FUNCTOR_verb1;        /* verb/1 */
  1740. static functor_t FUNCTOR_verb2;        /* verb/2 */
  1741. static functor_t FUNCTOR_verbatim2;    /* verbatim/2 */
  1742. static functor_t FUNCTOR_verbatim1;    /* verbatim/1 */
  1743. static functor_t FUNCTOR_dot2;        /* ./2 */
  1744. static functor_t FUNCTOR_brace1;    /* {}/1 */
  1745. static functor_t FUNCTOR_cmd1;        /* \/1 */
  1746. static functor_t FUNCTOR_cmd2;        /* \/2 */
  1747. static functor_t FUNCTOR_cmd3;        /* \/3 */
  1748. static functor_t FUNCTOR_env2;        /* env/2 */
  1749. static functor_t FUNCTOR_env3;        /* env/3 */
  1750. static functor_t FUNCTOR_env4;        /* env/4 */
  1751. static functor_t FUNCTOR_math1;        /* $/1 */
  1752. static functor_t FUNCTOR_mathenv1;    /* $$/1 */
  1753. static functor_t FUNCTOR_html1;        /* html/1 */
  1754. static functor_t FUNCTOR_html3;        /* html/3 */
  1755. static functor_t FUNCTOR_nospace1;    /* nospace/1 */
  1756. static atom_t     ATOM_begin_group;    /* '\{' */
  1757. static atom_t     ATOM_end_group;    /* '\}' */
  1758. static atom_t     ATOM_nbsp;        /* '~' */
  1759. static atom_t     ATOM_par;        /* 'par' */
  1760. static atom_t     ATOM_star;        /* * */
  1761. static atom_t     ATOM_minus;        /* - */
  1762. static atom_t     ATOM_space;        /* ' ' */
  1763. static atom_t     ATOM_nl;        /* '\n' */
  1764. static atom_t     ATOM_nil;        /* [] */
  1765.  
  1766. static void
  1767. initPrologConstants()
  1768. { FUNCTOR_verb2     = PL_new_functor(PL_new_atom("verb"), 2);
  1769.   FUNCTOR_verb1     = PL_new_functor(PL_new_atom("verb"), 1);
  1770.   FUNCTOR_verbatim2 = PL_new_functor(PL_new_atom("verbatim"), 2);
  1771.   FUNCTOR_verbatim1 = PL_new_functor(PL_new_atom("verbatim"), 1);
  1772.   FUNCTOR_dot2        = PL_new_functor(PL_new_atom("."), 2);
  1773.   FUNCTOR_brace1    = PL_new_functor(PL_new_atom("{}"), 1);
  1774.   FUNCTOR_cmd1      = PL_new_functor(PL_new_atom("\\"), 1);
  1775.   FUNCTOR_cmd2      = PL_new_functor(PL_new_atom("\\"), 2);
  1776.   FUNCTOR_cmd3      = PL_new_functor(PL_new_atom("\\"), 3);
  1777.   FUNCTOR_env2      = PL_new_functor(PL_new_atom("env"), 2);
  1778.   FUNCTOR_env3      = PL_new_functor(PL_new_atom("env"), 3);
  1779.   FUNCTOR_env4      = PL_new_functor(PL_new_atom("env"), 4);
  1780.   FUNCTOR_math1        = PL_new_functor(PL_new_atom("$"), 1);
  1781.   FUNCTOR_mathenv1  = PL_new_functor(PL_new_atom("$$"), 1);
  1782.   FUNCTOR_html1     = PL_new_functor(PL_new_atom("html"), 1);
  1783.   FUNCTOR_html3     = PL_new_functor(PL_new_atom("html"), 3);
  1784.   FUNCTOR_nospace1  = PL_new_functor(PL_new_atom("nospace"), 1);
  1785.  
  1786.   ATOM_begin_group  = PL_new_atom("\\{");
  1787.   ATOM_end_group    = PL_new_atom("\\}");
  1788.   ATOM_par        = PL_new_atom("par");
  1789.   ATOM_nbsp        = PL_new_atom("~");
  1790.   ATOM_star        = PL_new_atom("*");
  1791.   ATOM_minus        = PL_new_atom("-");
  1792.   ATOM_space        = PL_new_atom(" ");
  1793.   ATOM_nl        = PL_new_atom("\n");
  1794.   ATOM_nil        = PL_new_atom("[]");
  1795. }
  1796.  
  1797. static ppcontext ppctx;
  1798.  
  1799. static foreign_t
  1800. pl_put_tex_token(term_t term)
  1801. { token t;
  1802.   atom_t atom;
  1803.   functor_t f;
  1804.   static last_is_word = FALSE;
  1805.   
  1806.   t.type = -1;
  1807.  
  1808.   if ( PL_get_atom(term, &atom) )
  1809.   { if ( atom == ATOM_begin_group )
  1810.     { t.type = TOK_BEGIN_GROUP;
  1811.       t.value.string = "{";
  1812.     } else if ( atom == ATOM_end_group )
  1813.     { t.type = TOK_END_GROUP;
  1814.       t.value.string = "}";
  1815.     } else if ( atom == ATOM_space )
  1816.     { t.type = TOK_SPACE;
  1817.       t.value.string = " ";
  1818.     } else if ( atom == ATOM_nl )
  1819.     { t.type = TOK_LINE;
  1820.       t.value.string = "\n";
  1821.     } else
  1822.     { if ( last_is_word )        /* regenerate the space tokens */
  1823.       { t.type = TOK_SPACE;
  1824.     t.value.string = " ";
  1825.     
  1826.     put_token(&t, &ppctx);
  1827.       } else
  1828.     last_is_word = TRUE;
  1829.       t.type = TOK_WORD;
  1830.       t.value.string = (char *)PL_atom_chars(atom);
  1831.     }
  1832.   } else if ( PL_get_functor(term, &f) )
  1833.   { term_t arg = PL_new_term_ref();
  1834.     char *s;
  1835.  
  1836.     if ( f == FUNCTOR_verb2 || f == FUNCTOR_verbatim2 )
  1837.     { if ( PL_get_arg(1, term, arg) && PL_get_chars(arg, &s, CVT_ATOMIC) )
  1838.       { t.context = s;
  1839.  
  1840.     if ( PL_get_arg(2, term, arg) && PL_get_chars(arg, &s, CVT_ATOMIC) )
  1841.     { t.value.string = s;
  1842.       t.type = (f == FUNCTOR_verb2 ? TOK_VERB : TOK_VERBATIM);
  1843.     }
  1844.       }
  1845.     } else if ( f == FUNCTOR_cmd1 )
  1846.     { char *cname;
  1847.  
  1848.       if ( PL_get_arg(1, term, arg) && PL_get_chars(arg, &cname, CVT_ATOMIC) )
  1849.       { command g;
  1850.  
  1851.     t.type = TOK_CMD;
  1852.     t.value.cmd = &g;
  1853.     g.flags = 0;
  1854.     g.arguments = 0;
  1855.  
  1856.     g.command = lookupCommand(cname);
  1857.     if ( !g.command )
  1858.     { fprintf(stderr, "[WARNING: Undefined command: %s]\n", cname);
  1859.       g.command = newCommand(cname);
  1860.     }
  1861.       }
  1862.     } else if ( f == FUNCTOR_cmd2 || f == FUNCTOR_cmd3 ||
  1863.         f == FUNCTOR_env2 || f == FUNCTOR_env3 )
  1864.     { char *cname;
  1865.       command g;
  1866.       environment e;
  1867.       int isenv = (f == FUNCTOR_env2 || f == FUNCTOR_env3);
  1868.       int ismod = (f == FUNCTOR_cmd3 || f == FUNCTOR_env3);
  1869.       term_t alist = PL_new_term_ref();
  1870.  
  1871.       if ( isenv )
  1872.       { t.type = TOK_BEGIN_ENV;
  1873.     t.value.env = &e;
  1874.     e.flags = 0;
  1875.       } else
  1876.       { t.type = TOK_CMD;
  1877.     t.value.cmd = &g;
  1878.     g.flags = 0;
  1879.       }
  1880.  
  1881.       if ( ismod )
  1882.       { PL_get_arg(2, term, arg);
  1883.     if ( PL_get_atom(arg, &atom) && atom == ATOM_star )
  1884.     { if ( isenv )
  1885.         e.flags |= CMD_MODIFY;
  1886.       else
  1887.         g.flags |= CMD_MODIFY;
  1888.     }
  1889.     PL_get_arg(3, term, alist);
  1890.       } else
  1891.     PL_get_arg(2, term, alist);
  1892.  
  1893.       PL_get_arg(1, term, arg);
  1894.       if ( PL_get_atom_chars(arg, &cname) )
  1895.       { int n;
  1896.     term_t a2 = PL_new_term_ref();
  1897.     int argn;
  1898.     char **args;
  1899.  
  1900.     if ( isenv )
  1901.     { e.environment = lookupEnvironment(cname);
  1902.       if ( !e.environment )
  1903.       { fprintf(stderr, "[WARNING: Undefined environment: %s]\n", cname);
  1904.         e.environment = newEnvironment(cname);
  1905.       }
  1906.       argn = e.environment->arg_count;
  1907.       args = e.arguments = alloca(sizeof(cmd_arg)*argn);
  1908.     } else
  1909.     { g.command = lookupCommand(cname);
  1910.       if ( !g.command )
  1911.       { fprintf(stderr, "[WARNING: Undefined command: %s]\n", cname);
  1912.         g.command = newCommand(cname);
  1913.       }
  1914.       argn = g.command->arg_count;
  1915.       args = g.arguments = alloca(sizeof(cmd_arg)*argn);
  1916.     }
  1917.  
  1918.     for(n=0; n<argn; n++)
  1919.     { if ( PL_get_list(alist, arg, alist) &&
  1920.            PL_get_arg(1, arg, a2) &&
  1921.            PL_get_chars(a2, &s, CVT_ATOMIC) )
  1922.         args[n] = s;
  1923.       else
  1924.         args[n] = NULL;
  1925.     }
  1926.       }
  1927.     } else if ( f == FUNCTOR_math1 || f == FUNCTOR_mathenv1 )
  1928.     { term_t arg = PL_new_term_ref();
  1929.       char *s;
  1930.  
  1931.       if ( PL_get_arg(1, term, arg) &&
  1932.        PL_get_chars(arg, &s, CVT_ATOMIC) )
  1933.       { t.type = (f == FUNCTOR_math1 ? TOK_MATH : TOK_MATH_ENV);
  1934.     t.value.string = s;
  1935.       }
  1936.     }
  1937.   }
  1938.  
  1939.   if ( t.type != TOK_WORD )
  1940.     last_is_word = FALSE;
  1941.  
  1942.   if ( t.type >= 0 )
  1943.   { put_token(&t, &ppctx);
  1944.     PL_succeed;
  1945.   }
  1946.  
  1947.   return PL_warning("put_tex_token/1: instantiation error");
  1948. }
  1949.  
  1950.  
  1951. static void
  1952. tex2pl_from_string(const char *str, term_t tokens)
  1953. { pl_context ctx;
  1954.   Input fd;
  1955.  
  1956.   ctx.list       = PL_copy_term_ref(tokens);
  1957.   ctx.head       = PL_new_term_ref();
  1958.   ctx.envnesting = 0;
  1959.   ctx.prev_type0 = TOK_EOF;
  1960.   ctx.prev_type1 = TOK_EOF;
  1961.  
  1962.   fd = openInputString(str);
  1963.   parseTeX(fd, build_list, &ctx);
  1964.   closeInput(fd);
  1965. }
  1966.  
  1967.  
  1968. static foreign_t
  1969. pl_tex_atom_to_tokens(term_t txt, term_t tokens)
  1970. { char *s;
  1971.  
  1972.   if ( PL_get_chars(txt, &s, CVT_ALL) )
  1973.   { tex2pl_from_string(s, tokens);
  1974.  
  1975.     PL_succeed;
  1976.   }
  1977.  
  1978.   PL_fail;
  1979. }
  1980.  
  1981.  
  1982.  
  1983. static void
  1984. build_arguments(term_t alist, int nargs, CmdArg argspec, char **args)
  1985. { int ga = 0;                /* goal argument */
  1986.   term_t tmp = PL_new_term_ref();
  1987.  
  1988.   for( ; ga < nargs; ga++ )
  1989.   { PL_unify_list(alist, tmp, alist);
  1990.  
  1991.     if ( argspec[ga].flags & CA_OPTIONAL )
  1992.     { if ( args[ga] == NULL )
  1993.       { PL_unify_atom(tmp, ATOM_nil);
  1994.       } else
  1995.       { if ( argspec[ga].flags & CA_TEXT )
  1996.     { term_t arg = PL_new_term_ref();
  1997.  
  1998.       tex2pl_from_string(args[ga], arg);
  1999.       PL_unify_term(tmp,        /* [text] */
  2000.             PL_FUNCTOR, FUNCTOR_dot2,
  2001.             PL_TERM,    arg,
  2002.             PL_ATOM,    ATOM_nil);
  2003.     } else
  2004.     { PL_unify_term(tmp,        /* [text] */
  2005.             PL_FUNCTOR, FUNCTOR_dot2,
  2006.             PL_CHARS,   args[ga],
  2007.             PL_ATOM,    ATOM_nil);
  2008.     }
  2009.       }
  2010.     } else
  2011.     { if ( argspec[ga].flags & CA_TEXT )
  2012.       { term_t arg = PL_new_term_ref();
  2013.  
  2014.     tex2pl_from_string(args[ga], arg);
  2015.     PL_unify_term(tmp,        /* {text} */
  2016.               PL_FUNCTOR, FUNCTOR_brace1,
  2017.               PL_TERM,   arg);
  2018.  
  2019.       } else
  2020.       { PL_unify_term(tmp,        /* {text} */
  2021.               PL_FUNCTOR, FUNCTOR_brace1,
  2022.               PL_CHARS,   args[ga]);
  2023.       }
  2024.     }
  2025.   }
  2026.  
  2027.   PL_unify_nil(alist);
  2028. }
  2029.  
  2030.  
  2031. static void
  2032. popStack(PlContext ctx)
  2033. { if ( ctx->envnesting > 0 )
  2034.     ctx->list = ctx->stack[--ctx->envnesting];
  2035.   else
  2036.     error(ERR_ENV_UNDERFLOW, texfile(), texline());
  2037. }
  2038.  
  2039.  
  2040. static void
  2041. build_list(Token t, void *context)
  2042. { PlContext ctx = context;
  2043.  
  2044.   DEBUG(1, put_token(t, &ppctx));
  2045.  
  2046.   switch(t->type)
  2047.   { case TOK_EOF:
  2048.       PL_unify_nil(ctx->list);
  2049.       return;
  2050.     case TOK_SPACE:
  2051.     case TOK_LINE:
  2052.       goto out;
  2053.   }
  2054.  
  2055.   if ( (ctx->prev_type0 == TOK_SPACE || ctx->prev_type0 == TOK_LINE) &&
  2056.        (ctx->prev_type1 != TOK_WORD || t->type != TOK_WORD) )
  2057.   { atom_t a = (ctx->prev_type0 == TOK_SPACE ? ATOM_space : ATOM_nl);
  2058.     
  2059.     PL_unify_list(ctx->list, ctx->head, ctx->list);
  2060.     PL_unify_atom(ctx->head, a);
  2061.   }
  2062.  
  2063.   switch(t->type)
  2064.   { case TOK_END_GROUP:
  2065.     case TOK_END_ENV:
  2066.       PL_unify_nil(ctx->list);
  2067.       popStack(ctx);
  2068.       goto out;
  2069.   }
  2070.  
  2071.   PL_unify_list(ctx->list, ctx->head, ctx->list);
  2072.  
  2073.   switch(t->type)
  2074.   { case TOK_BEGIN_ENV:
  2075.     { Environment e   = t->value.env;
  2076.       atom_t modified = (e->flags & CMD_MODIFY ? ATOM_star : ATOM_minus);
  2077.       term_t clist    = PL_new_term_ref();
  2078.       term_t alist    = PL_new_term_ref();
  2079.  
  2080.       if ( e->environment->flags & CMD_MODIFY )
  2081.       { PL_unify_term(ctx->head,
  2082.               PL_FUNCTOR, FUNCTOR_env4,
  2083.               PL_CHARS,   e->environment->name,
  2084.               PL_ATOM,      modified,
  2085.               PL_TERM,      alist,
  2086.               PL_TERM,      clist);
  2087.       } else
  2088.       { PL_unify_term(ctx->head,
  2089.               PL_FUNCTOR, FUNCTOR_env3,
  2090.               PL_CHARS,   e->environment->name,
  2091.               PL_TERM,      alist,
  2092.               PL_TERM,      clist);
  2093.       }
  2094.  
  2095.       build_arguments(alist,        /* environment arguments */
  2096.               e->environment->arg_count,
  2097.               e->environment->arguments,
  2098.               e->arguments);
  2099.  
  2100.       PL_reset_term_refs(alist);
  2101.                     /* contents of the environment */
  2102.       if ( ctx->envnesting >= MAXENVNESTING )
  2103.     error(ERR_ENV_NESTING, texfile(), texline());
  2104.       ctx->stack[ctx->envnesting++] = ctx->list;
  2105.       ctx->list = clist;        /* no need to copy */
  2106.  
  2107.       break;
  2108.     }
  2109.     case TOK_CMD:
  2110.     { Command g       = t->value.cmd;
  2111.       term_t alist    = PL_new_term_ref();
  2112.       term_t modified = (g->flags & CMD_MODIFY ? ATOM_star : ATOM_minus);
  2113.       
  2114.       if ( g->command->flags & CMD_MODIFY )
  2115.       { PL_unify_term(ctx->head,
  2116.               PL_FUNCTOR, FUNCTOR_cmd3,
  2117.               PL_CHARS,   g->command->name,
  2118.               PL_ATOM,      modified,
  2119.               PL_TERM,      alist);
  2120.       } else
  2121.       { if ( g->command->arg_count == 0 )
  2122.     { PL_unify_term(ctx->head,
  2123.             PL_FUNCTOR, FUNCTOR_cmd1,
  2124.             PL_CHARS,   g->command->name);
  2125.       PL_reset_term_refs(alist);
  2126.  
  2127.       break;
  2128.     } else
  2129.     { PL_unify_term(ctx->head,
  2130.             PL_FUNCTOR, FUNCTOR_cmd2,
  2131.             PL_CHARS,   g->command->name,
  2132.             PL_TERM,    alist);
  2133.     }
  2134.       }
  2135.  
  2136.       build_arguments(alist,
  2137.               g->command->arg_count,
  2138.               g->command->arguments,
  2139.               g->arguments);
  2140.  
  2141.       PL_reset_term_refs(alist);
  2142.       break;
  2143.     }
  2144.     case TOK_BEGIN_GROUP:
  2145.       if ( ctx->envnesting >= MAXENVNESTING )
  2146.     error(ERR_ENV_NESTING, texfile(), texline());
  2147.       ctx->stack[ctx->envnesting++] = ctx->list;
  2148.       ctx->list = PL_copy_term_ref(ctx->head);
  2149.       break;
  2150.     case TOK_MATH:
  2151.       PL_unify_term(ctx->head,
  2152.             PL_FUNCTOR, FUNCTOR_math1,
  2153.             PL_STRING,  t->value.string);
  2154.       break;
  2155.     case TOK_MATH_ENV:
  2156.       PL_unify_term(ctx->head,
  2157.             PL_FUNCTOR, FUNCTOR_mathenv1,
  2158.             PL_STRING,  t->value.string);
  2159.       break;
  2160.     case TOK_VERB:
  2161.       PL_unify_term(ctx->head,
  2162.             PL_FUNCTOR, FUNCTOR_verb2,
  2163.             PL_CHARS,    t->context,
  2164.             PL_STRING,  t->value.string);
  2165.       break;
  2166.     case TOK_VERBATIM:
  2167.       PL_unify_term(ctx->head,
  2168.             PL_FUNCTOR, FUNCTOR_verbatim2,
  2169.             PL_CHARS,    t->context,
  2170.             PL_STRING,  t->value.string);
  2171.       break;
  2172.     case TOK_PAR:
  2173.       PL_unify_term(ctx->head,
  2174.             PL_FUNCTOR, FUNCTOR_cmd1,
  2175.             PL_ATOM,    ATOM_par);
  2176.       break;
  2177.     case TOK_WORD:
  2178.       PL_unify_atom_chars(ctx->head, t->value.string);
  2179.       break;
  2180.   }
  2181.  
  2182. out:
  2183.   ctx->prev_type1 = ctx->prev_type0;
  2184.   ctx->prev_type0 = t->type;
  2185. }
  2186.  
  2187.  
  2188. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2189. Calls tex:prolog_function(cmd([Star], [Args]))
  2190. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2191.  
  2192. static void
  2193. cmd_prolog(Command g, Input fd, CallBack func, void *ctx)
  2194. { fid_t  f        = PL_open_foreign_frame();
  2195.   term_t t0       = PL_new_term_ref();
  2196.   term_t alist    = PL_new_term_ref();
  2197.   term_t modified = (g->flags & CMD_MODIFY ? ATOM_star : ATOM_minus);
  2198.   predicate_t p   = PL_predicate("prolog_function", 1, "tex");
  2199.  
  2200.   if ( g->command->flags & CMD_MODIFY )
  2201.   { PL_unify_term(t0,
  2202.           PL_FUNCTOR, FUNCTOR_cmd3,
  2203.           PL_CHARS,   g->command->name,
  2204.           PL_ATOM,    modified,
  2205.           PL_TERM,    alist);
  2206.   } else
  2207.   { if ( g->command->arg_count == 0 )
  2208.     { PL_unify_term(t0,
  2209.             PL_FUNCTOR, FUNCTOR_cmd1,
  2210.             PL_CHARS,   g->command->name);
  2211.     } else
  2212.     { PL_unify_term(t0,
  2213.             PL_FUNCTOR, FUNCTOR_cmd2,
  2214.             PL_CHARS,   g->command->name,
  2215.             PL_TERM,    alist);
  2216.     }
  2217.   }
  2218.  
  2219.   build_arguments(alist,
  2220.           g->command->arg_count,
  2221.           g->command->arguments,
  2222.           g->arguments);
  2223.  
  2224.   PL_call_predicate(NULL, TRUE, p, t0);
  2225.   PL_discard_foreign_frame(f);
  2226.  
  2227.   cmd_normal(g, fd, func, ctx);
  2228. }
  2229.  
  2230.  
  2231. foreign_t
  2232. pl_tex_tokens(term_t file, term_t tokens)
  2233. { char *fname;
  2234.  
  2235.   if ( PL_get_chars(file, &fname, CVT_ALL) )
  2236.   { pl_context ctx;
  2237.  
  2238.     ctx.list       = tokens;
  2239.     ctx.head       = PL_new_term_ref();
  2240.     ctx.envnesting = 0;
  2241.     ctx.prev_type0 = TOK_EOF;
  2242.     ctx.prev_type1 = TOK_EOF;
  2243.  
  2244.     parseTeXFile(fname, build_list, &ctx);
  2245.     PL_succeed;
  2246.   }
  2247.  
  2248.   PL_fail;
  2249. }
  2250.  
  2251.  
  2252. foreign_t
  2253. pl_tex_command_property(term_t name, term_t pre, term_t post)
  2254. { char *cname;
  2255.  
  2256.   if ( PL_get_atom_chars(name, &cname) )
  2257.   { CmdDescr cmd = lookupCommand(cname);
  2258.  
  2259.     if ( cmd &&
  2260.      PL_unify_integer(pre, cmd->pre_lines) &&
  2261.          PL_unify_integer(post, cmd->post_lines) )
  2262.       PL_succeed;
  2263.   }
  2264.  
  2265.   PL_fail;
  2266. }
  2267.  
  2268.  
  2269. foreign_t
  2270. pl_tex_debug(term_t old, term_t new)
  2271. { if ( PL_unify_integer(old, debuglevel) &&
  2272.        PL_get_integer(new, &debuglevel) )
  2273.     PL_succeed;
  2274.  
  2275.   PL_fail;
  2276. }
  2277.  
  2278.  
  2279. foreign_t
  2280. pl_tex_tell(term_t file)
  2281. { char *name;
  2282.  
  2283.   if ( PL_get_chars(file, &name, CVT_ALL) )
  2284.   { FILE *fd = (streq(name, "-") ? stdout : fopen(name, "w"));
  2285.     
  2286.     if ( fd )
  2287.     { ppctx.envnesting   = 0;        /* seperate predicate? */
  2288.       ppctx.last_type    = TOK_EOF;
  2289.       ppctx.line_pos     = 0;
  2290.       ppctx.newlines     = 0;
  2291.       ppctx.spaces       = 0;
  2292.       ppctx.verbatim     = FALSE;
  2293.       ppctx.left_margin  = 0;
  2294.       ppctx.right_margin = 72;
  2295.       ppctx.fd             = fd;
  2296.  
  2297.       PL_succeed;
  2298.     }
  2299.   }
  2300.  
  2301.   PL_fail;
  2302. }
  2303.  
  2304.  
  2305. foreign_t
  2306. pl_tex_told()
  2307. { fflush(ppctx.fd);
  2308.   if ( ppctx.fd != stdout )
  2309.     fclose(ppctx.fd);
  2310.   ppctx.fd = stdout;
  2311.  
  2312.   PL_succeed;
  2313. }
  2314.  
  2315.  
  2316. foreign_t
  2317. pl_tex_read_commands(term_t file)
  2318. { char *name;
  2319.  
  2320.   if ( PL_get_chars(file, &name, CVT_ALL) &&
  2321.        parseCmdSpecs(name) )
  2322.     PL_succeed;
  2323.  
  2324.   PL_fail;
  2325. }
  2326.  
  2327.  
  2328. foreign_t
  2329. pl_tex_declare(term_t spec)
  2330. { char *s;
  2331.  
  2332.   if ( PL_get_chars(spec, &s, CVT_ALL) &&
  2333.        parseCommandSpec("tex_declare/1", 0, s) )
  2334.     PL_succeed;
  2335.  
  2336.   PL_fail;
  2337. }
  2338.  
  2339.  
  2340. foreign_t
  2341. pl_tex_environment_function(term_t env, term_t func)
  2342. { char *s;
  2343.   EnvDescr e;
  2344.  
  2345.   if ( PL_get_atom_chars(env, &s) &&
  2346.        (e = lookupEnvironment(s)) &&
  2347.        e->fname )
  2348.     return PL_unify_atom_chars(func, e->fname);
  2349.  
  2350.   PL_fail;
  2351. }
  2352.  
  2353.  
  2354. foreign_t
  2355. pl_tex_command_function(term_t cmd, term_t func)
  2356. { char *s;
  2357.   CmdDescr c;
  2358.  
  2359.   if ( PL_get_atom_chars(cmd, &s) &&
  2360.        (c = lookupCommand(s)) &&
  2361.        c->fname )
  2362.     return PL_unify_atom_chars(func, c->fname);
  2363.  
  2364.   PL_fail;
  2365. }
  2366.  
  2367.  
  2368.          /*******************************
  2369.          *          HTML OUTPUT        *
  2370.          *******************************/
  2371.  
  2372. static void
  2373. output_n(PPContext pp, const char *s, int l)
  2374. { if ( l > 0 )
  2375.   { char buf[l+1];
  2376.       
  2377.     memcpy(buf, s, l);
  2378.     buf[l] = EOS;
  2379.     output(pp, "%s", buf);
  2380.   }
  2381. }
  2382.  
  2383.  
  2384. static void
  2385. output_html(PPContext pp, const char *s)
  2386. { int c;
  2387.   const char *from = s;
  2388.  
  2389.   for(; (c=*s); s++)
  2390.   { switch(c)
  2391.     { case '<':
  2392.     output_n(pp, from, s-from);
  2393.         from = s+1;
  2394.         output(pp, "%s", "<");
  2395.     break;
  2396.       case '>':
  2397.     output_n(pp, from, s-from);
  2398.         from = s+1;
  2399.         output(pp, "%s", ">");
  2400.     break;
  2401.       case '&':
  2402.     output_n(pp, from, s-from);
  2403.         from = s+1;
  2404.         output(pp, "%s", "&");
  2405.     break;
  2406.     }
  2407.   }
  2408.     
  2409.   output_n(pp, from, s-from);
  2410. }
  2411.  
  2412.  
  2413. static void
  2414. put_html_token(Token t, void *ctx)
  2415. { PPContext pp = ctx;
  2416.  
  2417.   switch(t->type)
  2418.   { case TOK_CMD:
  2419.     { outputBlank(pp);
  2420.       while(pp->newlines < t->prelines)
  2421.     output(pp, "\n");
  2422.       output(pp, "%s", t->value.string);
  2423.       while(pp->newlines < t->postlines)
  2424.     output(pp, "\n");
  2425.  
  2426.       break;
  2427.     }
  2428.     case TOK_VERBATIM:
  2429.     { pp->verbatim = TRUE;
  2430.       output(pp, "%s", t->value.string);
  2431.       pp->verbatim = FALSE;
  2432.       break;
  2433.     }
  2434.     case TOK_VERB:
  2435.     { outputBlank(pp);
  2436.       pp->verbatim = TRUE;
  2437.       output(pp, "%s", t->value.string);
  2438.       pp->verbatim = FALSE;
  2439.       
  2440.       break;
  2441.     }
  2442.     case TOK_SPACE:
  2443.       break;
  2444.     case TOK_LINE:
  2445.       break;
  2446.     case TOK_NOSPACEWORD:
  2447.       outputBlank(pp);
  2448.       output_html(pp, t->value.string);
  2449.       break;
  2450.     case TOK_WORD:
  2451.     { int pendingblank;
  2452.  
  2453.       if ( pp->last_type == TOK_LINE )
  2454.     pp->last_type = TOK_SPACE;
  2455.       pendingblank = (pp->last_type == TOK_SPACE);
  2456.       outputBlank(pp);            /* as space! */
  2457.       if ( pendingblank &&        /* no blanks in input: concatenate! */
  2458.        strlen(t->value.string) + pp->line_pos > pp->right_margin )
  2459.     nl(pp);
  2460.       output_html(pp, t->value.string);
  2461.       break;
  2462.     }
  2463.     case TOK_EOF:
  2464.       output(pp, "\n");
  2465.       break;
  2466.     default:
  2467.       assert(0);
  2468.   }
  2469.  
  2470.   pp->last_type = t->type;
  2471. }
  2472.  
  2473. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2474. Control output of HTML data format:
  2475.  
  2476.   html(Text, [Pre, Post])    Output a command
  2477.   verbatim(Text)        Output verbatim text
  2478.   verb(Text)            Output short text
  2479.   Atom                Output plain text
  2480. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  2481.  
  2482. static foreign_t
  2483. pl_put_html_token(term_t term)
  2484. { token t;
  2485.   char *s;
  2486.   atom_t atom;
  2487.   static last_is_word = FALSE;
  2488.  
  2489.   t.type = -1;
  2490.  
  2491.   if ( PL_is_functor(term, FUNCTOR_html3) )
  2492.   { term_t a = PL_new_term_ref();
  2493.  
  2494.     if ( PL_get_arg(1, term, a) &&
  2495.      PL_get_chars(a, &s, CVT_ATOMIC) &&
  2496.      PL_get_arg(2, term, a) &&
  2497.      PL_get_integer(a, &t.prelines) &&
  2498.      PL_get_arg(3, term, a) &&
  2499.      PL_get_integer(a, &t.postlines) )
  2500.     { t.type = TOK_CMD;
  2501.       t.value.string = s;
  2502.     }
  2503.   } else if ( PL_is_functor(term, FUNCTOR_html1) )
  2504.   { term_t a = PL_new_term_ref();
  2505.  
  2506.     if ( PL_get_arg(1, term, a) &&
  2507.      PL_get_chars(a, &s, CVT_ATOMIC) )
  2508.     { t.type = TOK_CMD;
  2509.       t.value.string = s;
  2510.       t.prelines = t.postlines = 0;
  2511.     }
  2512.   } else if ( PL_is_functor(term, FUNCTOR_verbatim1) )
  2513.   { term_t a = PL_new_term_ref();
  2514.  
  2515.     if ( PL_get_arg(1, term, a) &&
  2516.      PL_get_chars(a, &s, CVT_ATOMIC) )
  2517.     { t.type = TOK_VERBATIM;
  2518.       t.value.string = s;
  2519.     }
  2520.   } else if ( PL_is_functor(term, FUNCTOR_verb1) )
  2521.   { term_t a = PL_new_term_ref();
  2522.  
  2523.     if ( PL_get_arg(1, term, a) &&
  2524.      PL_get_chars(a, &s, CVT_ATOMIC) )
  2525.     { t.type = TOK_VERB;
  2526.       t.value.string = s;
  2527.     }
  2528.   } else if ( PL_is_functor(term, FUNCTOR_nospace1) )
  2529.   { term_t a = PL_new_term_ref();
  2530.  
  2531.     if ( PL_get_arg(1, term, a) &&
  2532.      PL_get_chars(a, &s, CVT_ATOMIC) )
  2533.     { t.type = TOK_NOSPACEWORD;
  2534.       t.value.string = s;
  2535.     }
  2536.   } else if ( PL_get_atom(term, &atom) )
  2537.   { if ( atom == ATOM_space )
  2538.     { t.type = TOK_SPACE;
  2539.       t.value.string = " ";
  2540.     } else if ( atom == ATOM_nl )
  2541.     { t.type = TOK_LINE;
  2542.       t.value.string = "\n";
  2543.     } else
  2544.     { if ( last_is_word )        /* regenerate the space tokens */
  2545.       { t.type = TOK_SPACE;
  2546.     t.value.string = " ";
  2547.     
  2548.     put_html_token(&t, &ppctx);
  2549.       } else
  2550.     last_is_word = TRUE;
  2551.  
  2552.       t.type = TOK_WORD;
  2553.       t.value.string = (char *)PL_atom_chars(atom);
  2554.     }
  2555.   } else if ( PL_get_chars(term, &s, CVT_ALL) )
  2556.   { if ( last_is_word )        /* regenerate the space tokens */
  2557.     { t.type = TOK_SPACE;
  2558.       t.value.string = " ";
  2559.     
  2560.       put_html_token(&t, &ppctx);
  2561.     } else
  2562.       last_is_word = TRUE;
  2563.  
  2564.     t.type = TOK_WORD;
  2565.     t.value.string = s;
  2566.   }
  2567.  
  2568.   if ( t.type != TOK_WORD )
  2569.     last_is_word = FALSE;
  2570.  
  2571.   if ( t.type >= 0 )
  2572.   { put_html_token(&t, &ppctx);
  2573.     PL_succeed;
  2574.   }
  2575.  
  2576.   return PL_warning("put_html_token/1: instantiation error");
  2577. }
  2578.  
  2579.          /*******************************
  2580.          *          ERRORS        *
  2581.          *******************************/
  2582.  
  2583. static void
  2584. error(int errno, const char *file, int line)
  2585. { fprintf(stderr,
  2586.       "[TeX tokeniser: %s:%d: %s]\n",
  2587.       file, line, tex_error_strings[errno]);
  2588.   switch(errno)
  2589.   { case ERR_RUNAWAY_ARGUMENT:
  2590.     case ERR_RUNAWAY_MATH:
  2591.     { char argstart[50];
  2592.       strncpy(argstart, texarg, 50);
  2593.       argstart[49] = EOS;
  2594.       fprintf(stderr, "Start: \"%s\"\n", argstart);
  2595.       break;
  2596.     }
  2597.   }
  2598.  
  2599.   exit(1);
  2600. }
  2601.  
  2602. static void
  2603. warn(int errno, const char *file, int line)
  2604. { fprintf(stderr,
  2605.       "WARNING: %s:%d: %s\n",
  2606.       file, line, tex_error_strings[errno]);
  2607. }
  2608.  
  2609.  
  2610. extern void install_ps(void);
  2611.  
  2612. install_t
  2613. install()
  2614. { initPrologConstants();
  2615.  
  2616.   PL_register_foreign("tex_tokens",           2, pl_tex_tokens,           0);
  2617.   PL_register_foreign("tex_command_property", 3, pl_tex_command_property, 0);
  2618.   PL_register_foreign("put_tex_token",        1, pl_put_tex_token,        0);
  2619.   PL_register_foreign("put_html_token",       1, pl_put_html_token,       0);
  2620.   PL_register_foreign("tex_debug",            2, pl_tex_debug,            0);
  2621.   PL_register_foreign("tex_tell",             1, pl_tex_tell,          0);
  2622.   PL_register_foreign("tex_told",             0, pl_tex_told,             0);
  2623.   PL_register_foreign("tex_read_commands",    1, pl_tex_read_commands,      0);
  2624.   PL_register_foreign("tex_declare",          1, pl_tex_declare,      0);
  2625.   PL_register_foreign("tex_environment_function",
  2626.                           2, pl_tex_environment_function,
  2627.                                       0);
  2628.   PL_register_foreign("tex_command_function", 2, pl_tex_command_function, 0);
  2629.   PL_register_foreign("tex_atom_to_tokens",   2, pl_tex_atom_to_tokens,   0);
  2630.  
  2631.   ppctx.envnesting   = 0;        /* seperate predicate? */
  2632.   ppctx.last_type    = TOK_EOF;
  2633.   ppctx.line_pos     = 0;
  2634.   ppctx.newlines     = 0;
  2635.   ppctx.spaces       = 0;
  2636.   ppctx.verbatim     = FALSE;
  2637.   ppctx.left_margin  = 0;
  2638.   ppctx.right_margin = 72;
  2639.   ppctx.fd         = stdout;
  2640.  
  2641.   install_ps();
  2642. }
  2643.  
  2644. #endif /*__SWI_PROLOG__*/
  2645.  
  2646. #ifdef __SWI_EMBEDDED__
  2647.  
  2648. PL_extension PL_extensions [] =
  2649. {
  2650. /*{ "name",    arity,  function,    PL_FA_<flags> },*/
  2651.  
  2652.   { NULL,    0,     NULL,        0 }    /* terminating line */
  2653. };
  2654.  
  2655.  
  2656. int
  2657. main(int argc, char **argv, char **env)
  2658. { char *av[argc+2];
  2659.   int i=0, ac = 0;
  2660.   predicate_t pred;
  2661.  
  2662.   av[ac++] = argv[i++];
  2663.   av[ac++] = "--";
  2664.   while( i<argc )
  2665.     av[ac++] = argv[i++];
  2666.   av[ac] = NULL;
  2667.  
  2668.   if ( !PL_initialise(ac, av, env) )
  2669.     PL_halt(1);
  2670.   signal(SIGINT, SIG_DFL);        /* don't catch interrupts */
  2671.   install();                /* install foreign functions */
  2672.  
  2673.   pred = PL_predicate("main", 0, "difftex");
  2674.   if ( PL_call_predicate(NULL, TRUE, pred, 0) )
  2675.     exit(0);
  2676.  
  2677.   exit(1);
  2678. }
  2679.  
  2680. #endif /*__SWI_EMBEDDED__*/
  2681.